import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.Stroke;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.nio.channels.SelectableChannel;
import java.sql.Savepoint;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;


public class VisualPositioner extends JFrame {
	private static final long serialVersionUID = 1L;
	
	public ArrayList<Vec2D> robots = new ArrayList<Vec2D>();
	public ArrayList<Vec2D> selected = new ArrayList<Vec2D>();
	public Vec2D center = Vec2D.cortezian(0, 0);
	Rect selectRect = null;
	boolean showText = true;
	Rect borderRect = null;
	
	DrawPanel dpanel = new DrawPanel();
	JTextField text = new JTextField();
	JTextArea output = new JTextArea();

	public VisualPositioner() {
		
		ML ml = new ML();
		dpanel.addMouseListener(ml);
		dpanel.addMouseMotionListener(ml);
		CA ca = new CA();
		dpanel.addComponentListener(ca);
		
		output.setSize(10, 200);
		
		setLayout(new BorderLayout());
		add(dpanel, BorderLayout.CENTER);
		
		JPanel cpn = new JPanel();
		add(cpn, BorderLayout.SOUTH);
		cpn.setLayout(new BorderLayout());
		cpn.add(text, BorderLayout.SOUTH);
		cpn.add(output, BorderLayout.CENTER);
		
		
		text.addKeyListener(new KeyListener() {
			
			ArrayList<String> history = new ArrayList<String>();
			ArrayList<String> ohistory = new ArrayList<String>();
			int chis = 0;
			int ochis= 0;
			
			@Override
			public void keyTyped(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyReleased(KeyEvent e) {
				// TODO Auto-generated method stub
				
			}
			
			@Override
			public void keyPressed(KeyEvent e) {
				//System.out.println("keyPressed : "+e.getKeyCode());
				/*ENTER*/
				if( e.getKeyCode()==10 ){
					if(text.getText().equals("")){
						output.setText("");
					}else{
						output.setText(text.getText()+"\n");
						output.setText(output.getText()+Command(text.getText()));
						history.add(text.getText());
						text.setText("");
						chis=-1;
						ohistory.add(output.getText());
						ochis=0;
						repaint();
					}
				}
				/*up*/
				if( e.getKeyCode()==38 ){
					if(e.isControlDown()){
						if(ochis<ohistory.size()-1)ochis++;
						output.setText(ohistory.get(ohistory.size()-1-ochis));
					}else{
						if(chis<history.size()-1)chis++;
						text.setText(history.get(history.size()-1-chis));
					}
				}
				/*down*/
				if( e.getKeyCode()==40 ){
					if(e.isControlDown()){
						if(ochis>0)ochis--;
						output.setText(ohistory.get(ohistory.size()-1-ochis));
					}else{
						if(chis>0)chis--;
						text.setText(history.get(history.size()-1-chis));
					}
				}
			}
			
			public String Command(String comm){
				if(comm.startsWith("+select")){
					String c = comm.substring("+select".length());
					if(c.trim().length()==0){
						return select_all();	
					}
					if(c.trim().split(" +").length==3){
						String[] cc = c.trim().split(" +");
						return add_select(Double.parseDouble(cc[0])*10, Double.parseDouble(cc[1])*10, Double.parseDouble(cc[2])*10);	
					}
					if(c.trim().split(" +").length==4){
						String[] cc = c.trim().split(" +");
						return add_select(Double.parseDouble(cc[0])*10, Double.parseDouble(cc[1])*10, Double.parseDouble(cc[2])*10, Double.parseDouble(cc[3])*10);	
					}
				}
				if(comm.startsWith("-select")){
					String c = comm.substring("-select".length());
					if(c.trim().length()==0){
						return select_clear();	
					}
					if(c.trim().split(" +").length==3){
						String[] cc = c.trim().split(" +");
						return remove_select(Double.parseDouble(cc[0])*10, Double.parseDouble(cc[1])*10, Double.parseDouble(cc[2])*10);	
					}
					if(c.trim().split(" +").length==4){
						String[] cc = c.trim().split(" +");
						return remove_select(Double.parseDouble(cc[0])*10, Double.parseDouble(cc[1])*10, Double.parseDouble(cc[2])*10, Double.parseDouble(cc[3])*10);	
					}
				}
				if(comm.startsWith("print")){
					String c = comm.substring("print".length());
					if(c.startsWith(" counter")) return countInfo();
					if(c.startsWith(" selected")) return printSelected();
					if(c.endsWith(" in polar")){
						Vec2D p = parseXY(c);
						return toPolar(p.x(), p.y());	
					}
					if(c.endsWith(" in xy")){
						Vec2D p = parseXY(c);
						return toCort(p.x(), p.y());	
					}
					if(c.endsWith(" in selected")){
						Vec2D p = parseXY(c);
						return fromSelected(p.x(), p.y());	
					}
				}
				if(comm.startsWith("add")){
					String c = comm.substring("add".length());
					if(c.endsWith(" +selected")){
						Vec2D p = parseXY(c);
						return addPointToSelected(p.x(), p.y());	
					}else{
						Vec2D p = parseXY(c);
						return addPoint(p.x(), p.y());	
					}
				}
				if(comm.startsWith("remove")){
					String c = comm.substring("remove".length());
					if(c.endsWith(" selected")){
						return removeSelected();	
					}else{
						XYR p = parseXYR(c);
						return remove(p.x, p.y, p.r);	
					}
				}
				if(comm.startsWith("save")){
					if(comm.equals("save")) return saveAllToFile("");
					if(comm.equals("save selected")) return saveSelectedToFile("");
					
					String c = comm.substring("save".length());
					if(c.startsWith(" selected")){
						String f = c.substring(" selected ".length());
						return saveSelectedToFile(f);
					}else{
						String f = c.substring(" ".length());
						return saveAllToFile(f);
					}
				}
				if(comm.startsWith("border") || comm.startsWith("-border") ){
					if(comm.startsWith("-border")){
						return removeBorder();
					}else{
						String c = comm.substring("border ".length());
						XYXY n = parseXYXY(c);
						setBorder(n.x, n.y, n.xx, n.yy);
					}
				}
				if(comm.startsWith("help")){
					String c = comm.substring("help".length());
					return help();
				}
				if(comm.equals("")){
					return "";
				}

				return "command not found: "+comm;
			}
			
			public Vec2D parseXY(String s){
				while(s.startsWith(" ")) s=s.substring(1);
				int i = s.indexOf(" ");
				if(i<0) return Vec2D.cortezian(0, 0);
				String sx = s.substring(0, i);
				int j = s.indexOf(" ", i+1);
				if(j<0) j = s.length();
				String sy = s.substring(i+1, j);
				return Vec2D.cortezian(Double.parseDouble(sx)*10, Double.parseDouble(sy)*10);
			}
			class XYR{ public double x; public double y; public double r; }
			class XYXY{ public double x; public double y; public double xx; public double yy;}
			public XYR parseXYR(String s){
				while(s.startsWith(" ")) s=s.substring(1);
				XYR xyr = new XYR();
				int i = s.indexOf(" ");
				if(i<0) return xyr;
				String sx = s.substring(0, i);
				int j = s.indexOf(" ", i+1);
				if(j<0) return xyr;
				String sy = s.substring(i+1, j);
				int k = s.indexOf(" ", j+1);
				if(k<0) k=s.length();
				String sr = s.substring(j+1, k);
				xyr.x = Double.parseDouble(sx)*10;
				xyr.y = Double.parseDouble(sy)*10;
				xyr.r = Double.parseDouble(sr)*10;
				return xyr;
			}
			public XYXY parseXYXY(String s){
				while(s.startsWith(" ")) s=s.substring(1);
				XYXY xyxy = new XYXY();
				int i = s.indexOf(" ");
				if(i<0) return xyxy;
				String sx = s.substring(0, i);
				int j = s.indexOf(" ", i+1);
				if(j<0) return xyxy;
				String sy = s.substring(i+1, j);
				int k = s.indexOf(" ", j+1);
				if(k<0) k=s.length();
				String sr = s.substring(j+1, k);
				int kk = s.indexOf(" ", k+1);
				if(kk<0) kk=s.length();
				String srr = s.substring(k+1, kk);
				xyxy.x = Double.parseDouble(sx)*10;
				xyxy.y = Double.parseDouble(sy)*10;
				xyxy.xx = Double.parseDouble(sr)*10;
				xyxy.yy = Double.parseDouble(srr)*10;
				return xyxy;
			}
			
			public String addPoint(double x, double y){
				Vec2D v = Vec2D.cortezian(x,  y);
				robots.add(v);
				return "point ("+x/10+","+y/10+") added.";
			}
			public String addPointToSelected(double x, double y){
				if(selected.size()==0) return "No selected points";
				Vec2D v = Vec2D.cortezian(x,  y).add(selected.get(selected.size()-1));
				robots.add(v);
				return "point ("+x/10+","+y/10+") added.";
			}
			public String remove(double x, double y, double r){
				Vec2D v = Vec2D.cortezian(x,  y);
				for(Vec2D b: robots){
					if(b.sub(v).len()<=r){
						robots.remove(b);
						if(selected.contains(b))selected.remove(b);
						return "point ("+b.x()/10+","+b.y()/10+") removed.";
					}
				}
				return "point for remove does not found";
			}
			public String removeSelected(){
				int i=0;
				for(Vec2D b: selected){
					robots.remove(b);
					i++;
				}
				selected.clear();
				return ""+i+" pointes removed.";
			}
			private String F(double s){ return String.format("%.2f", s); }
			public String countInfo(){
				return "Total: "+robots.size()+"\nSelected: "+selected.size();
			}
			public String toPolar(double x, double y){
				Vec2D v = Vec2D.cortezian(x,  y);
				return "Polar: ang="+F(Vec2D.r2d(v.ang()))+" deg, len="+F(v.len()/10); 
			}
			public String toCort(double a, double l){
				Vec2D v = Vec2D.polar(Vec2D.d2r(a), l);
				return "Cortezian: x="+F(v.x()/10)+", y="+F(v.y()/10); 
			}
			public String printPoint(Vec2D p){
				return "point: x="+F(p.x()/10)+", y="+F(p.y()/10)+", ang="+F(Vec2D.r2d(p.ang()))+" deg, len="+F(p.len()/10);
			}
			public String printSelected(){
				String s="";
				for(Vec2D r: selected){
					s+=printPoint(r)+"\n";
				}
				return s;
			}
			public String fromSelected(double x, double y){
				Vec2D v = Vec2D.cortezian(x,  y);
				String s="";
				for(Vec2D r: selected){
					s+="Point: "+printPoint(r)+": from "+printPoint(v.sub(r))+"; to "+printPoint(r.sub(v))+"\n";
				}
				return s;
			}
			public String add_select(double x, double y, double r){
				Vec2D v = Vec2D.cortezian(x,  y);
				for(Vec2D b: robots){
					if(b.sub(v).len()<=r){
						if(selected.contains(b)==false){
							selected.add(b);
							return "point ("+F(b.x()/10)+","+F(b.y()/10)+") selected.";
						}else{
							return "point ("+F(b.x()/10)+","+F(b.y()/10)+") has been selected.";
						}
					}
				}
				return "point for select does not found";
			}
			public String add_select(double x1, double y1,double x2, double y2){
				Vec2D v1 = Vec2D.cortezian(x1,  y1);
				Vec2D v2 = Vec2D.cortezian(x2,  y2);
				Rect rect = new Rect(v1, v2);
				String s = "";
				for(Vec2D b: robots){
					if(rect.inside(b)){
						if(selected.contains(b)==false){
							selected.add(b);
							s+="point ("+F(b.x()/10)+","+F(b.y()/10)+") selected."+"\n";
						}else{
							s+="point ("+F(b.x()/10)+","+F(b.y()/10)+") has been selected."+"\n";
						}
					}
				}
				if(s.length()==0) return "points for select does not found";
				return s;
			}
			public String remove_select(double x, double y, double r){
				Vec2D v = Vec2D.cortezian(x,  y);
				//System.out.println("v="+v);
				for(Vec2D b: robots){
					//System.out.println("b="+b+",   b.sub(v).len()"+b.sub(v).len()+" ? r="+r);
					if(b.sub(v).len()<=r){
						if(selected.contains(b)==true){
							selected.remove(b);
							return "point ("+F(b.x()/10)+","+F(b.y()/10)+") removed from selected.";
						}else{
							return "point ("+F(b.x()/10)+","+F(b.y()/10)+") has not been selected.";
						}
					}
				}
				return "point not found";
			}
			public String remove_select(double x1, double y1,double x2, double y2){
				Vec2D v1 = Vec2D.cortezian(x1,  y1);
				Vec2D v2 = Vec2D.cortezian(x2,  y2);
				Rect rect = new Rect(v1, v2);
				String s = "";
				for(Vec2D b: robots){
					if(rect.inside(b)){
						if(selected.contains(b)==true){
							selected.remove(b);
							s+="point ("+F(b.x()/10)+","+F(b.y()/10)+") removed from selected."+"\n";
						}else{
							s+="point ("+F(b.x()/10)+","+F(b.y()/10)+") has not been selected."+"\n";
						}
					}
				}
				if(s.length()==0) return "points not found";
				return s;
			}
			public String select_all(){
				selected.clear();
				selected.addAll(robots);
				return "All points selected";
			}
			public String select_clear(){
				selected.clear();
				return "Select cleared";
			}
			
			public String VecToRobot(Vec2D v){
				return "robot EDU.gatech.cc.is.abstractrobot.MultiForageN150Sim CoordinatedFormation "+ F(v.x()/10) +" "+ F(v.y()/10) +" 0 x00FF00 xFF0000 2";
			}
			public String saveAllToFile(String filename){
				String s = "save not implemented. file name is "+filename+"";
				for(Vec2D r: robots){
					s += "\n" + VecToRobot(r);
				}
				return s;
			}
			public String saveSelectedToFile(String filename){
				String s = "save not implemented. file name is "+filename+"";
				for(Vec2D r: selected){
					s += "\n" + VecToRobot(r);
				}
				return s;
			}
			public String setBorder(double x, double y, double xx, double yy){
				borderRect = new Rect(Vec2D.cortezian(x, y), Vec2D.cortezian(xx, yy));
				return "Border defined";
			}
			public String removeBorder(){
				borderRect = null;
				return "Border undefined";
			}
			
			public String help(){
				return 		"help - this help \n"
						+	"+select - select all points \n"
						+	"+select x y radius - select point near x y \n"
						+	"+select x y x y - select all points in side rectangle defined by top-left bottom-right \n"
						+	"-select - remove selection from all points \n"
						+	"-select x y radius - remove selection from point near x y \n"
						+	"-select x y x y - remove selection from all points in side rectangle defined by top-left bottom-right \n"
						+	"print selected - print selected points \n"
						+	"print counters - print numbers of points (total and selected) \n"
						+	"print x y in polar - print point x,y in polar form \n"
						+	"print ang len in xy - print point ang,len in cortezian form \n"
						+	"print x y in selected - print x,y point related to selected points \n"
						+	"add x y - add new x,y point \n"
						+	"add x y +selected - add new point ((x,y) + selected) \n"
						+	"remove x y radius - remove poiny near x y \n"
						+	"remove selected - remove selected point \n"
						+	"save filename - save points to file \n"
						+	"save selected filename - save selected points to file \n"
						+	"border x y x y - draw border with corrdinates left-top, right-bottom \n"
						+	"-border - remove border \n"
						;
			}
		});
	}
	
	Dimension _getSize(){ return dpanel.getSize(); }
	
	class DrawPanel extends JPanel{
		@Override
		public void paint(Graphics g) {
			//super.paint(g);
			g.clearRect(0, 0, getSize().width, getSize().height);
			paint2D((Graphics2D)g);
		}
	}
	
	private void printGrid(Graphics2D g, int inc){
		for(int x=(int)center.x()+inc; x<_getSize().width; x+=inc){
			g.drawLine((int)x, (int)0, (int)x, (int)_getSize().height);
		}
		for(int x=(int)center.x(); x>=0; x-=inc){
			g.drawLine((int)x, (int)0, (int)x, (int)_getSize().height);
		}
		for(int y=(int)center.y()+inc; y<_getSize().height; y+=inc){
			g.drawLine((int)0, (int)y, (int)_getSize().width, (int)y);
		}
		for(int y=(int)center.y(); y>=0; y-=inc){
			g.drawLine((int)0, (int)y, (int)_getSize().width, (int)y);
		}
	}
	private void printGrid(Graphics2D g){
		g.setColor(Color.lightGray);
		printGrid(g, 10);
		g.setColor(Color.gray);
		printGrid(g, 100);
		Stroke s = g.getStroke();
		g.setStroke(new BasicStroke(2));
		g.drawLine((int)0, (int)center.y(), (int)_getSize().width, (int)center.y());
		g.drawLine((int)center.x(), (int)0, (int)center.x(), (int)_getSize().height);
		g.setStroke(s);
	}
	public void paint2D(Graphics2D g) {
		printGrid(g);
		if( borderRect!=null ){
			Vec2D p1 = borderRect.iv().p1.add(center);
			Vec2D p2 = borderRect.iv().p2.add(center).sub(p1);
			g.setColor(Color.yellow);
			g.drawRect((int)p1.x(), (int)p1.y(), (int)p2.x(), (int)p2.y());
		}
		int ri=0;
		for(Vec2D r : robots){
			if(selected.contains(r)){
				g.setColor(Color.red);
			}else{
				g.setColor(Color.black);
			}
			Vec2D p = r.add(center);
			g.fillOval((int)p.x()-3, (int)p.y()-3, 6,6);
			p=p.add(Vec2D.cortezian(5, -5));
			if(showText)
				g.drawString(""+ri+") ("+String.format("%.2f", r.x()/10)+","+String.format("%.2f", r.y()/10)+")", (int)p.x(), (int)p.y());
			ri++;
		}
		if( selectRect!=null ){
			Vec2D p1 = selectRect.iv().p1.add(center);
			Vec2D p2 = selectRect.iv().p2.add(center).sub(p1);
			g.setColor(Color.green);
			g.drawRect((int)p1.x(), (int)p1.y(), (int)p2.x(), (int)p2.y());
		}
	}
	
	class CA implements ComponentListener{

		@Override
		public void componentHidden(ComponentEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void componentMoved(ComponentEvent arg0) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void componentResized(ComponentEvent e) {
			center = Vec2D.cortezian(_getSize().width, _getSize().height).scale(0.5);
		}

		@Override
		public void componentShown(ComponentEvent arg0) {
			// TODO Auto-generated method stub
			
		}
		
	}
	
	class Rect{
		public Vec2D p1, p2;
		public Rect(Vec2D pp1, Vec2D pp2){ p1=pp1; p2=pp2; }
		boolean inside(Vec2D p){
			Vec2D p1 = iv().p1;
			Vec2D p2 = iv().p2;
			return p1.x()<=p.x() && p.x()<=p2.x() && p1.y()<=p.y() && p.y()<=p2.y(); 
		}
		public Rect iv(){
			Vec2D p1 = Vec2D.cortezian(Math.min(this.p1.x(), this.p2.x()), Math.min(this.p1.y(), this.p2.y()));
			Vec2D p2 = Vec2D.cortezian(Math.max(this.p1.x(), this.p2.x()), Math.max(this.p1.y(), this.p2.y()));
			return new Rect(p1,p2);
		}
	}
	
	class ML implements MouseListener,MouseMotionListener{
		Vec2D pressed = null;
		Vec2D prevMove = null;
		@Override
		public void mouseClicked(MouseEvent e) {
			Vec2D click = Vec2D.cortezian(e.getX(), e.getY()).sub(center);
			boolean onrobot = false;
			for(Vec2D r: robots){
				if(r.sub(click).len()<6){
					if(e.getButton() == MouseEvent.BUTTON1){
						if(selected.contains(r)) selected.remove(r);
						else selected.add(r);
						onrobot = true;
					}else{
						if(selected.contains(r)) selected.remove(r);
						robots.remove(r);
						onrobot = true;
						break;
					}
				}
			}
			if(onrobot==false){
				Vec2D nr = click.clone();
				selected.clear();
				if(e.getButton() == MouseEvent.BUTTON1){
					robots.add(nr);
					selected.add(nr);
				}
			}
			
			repaint();
		}

		@Override
		public void mouseEntered(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mouseExited(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public void mousePressed(MouseEvent e) {
			pressed = Vec2D.cortezian(e.getX(), e.getY()).sub(center);
			if(selected.isEmpty())
				selectRect = new Rect(pressed.clone(),  pressed.clone());
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			Vec2D.cortezian(e.getX(), e.getY()).sub(center);
			if(selectRect!=null){
				for(Vec2D r: robots){
					if(selectRect.inside(r)) selected.add(r);
				}
			}
			
			pressed = null;
			prevMove = null;
			selectRect = null;
			
			repaint();
		}

		@Override
		public void mouseDragged(MouseEvent e) {
			Vec2D click = Vec2D.cortezian(e.getX(), e.getY()).sub(center);
			if(selectRect!=null){
				selectRect.p2 = click.clone();
			}else{
				Vec2D moved = click.sub(pressed);
				for(Vec2D r: selected){
					Vec2D prevPos = r; 
					if(prevMove!=null) prevPos = r.sub(prevMove);
					r.set( prevPos.add(moved) );
				}
				prevMove = moved;
			}
			repaint();
		}

		@Override
		public void mouseMoved(MouseEvent e) {
			// TODO Auto-generated method stub
			
		}
		
	}
	
	public static void main(String[] args) {
		VisualPositioner vp = new VisualPositioner();
		vp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		vp.setSize(600, 400);
		vp.setVisible(true);
	}

}
